package com.example.lotterysystem.service.mq;


import cn.hutool.core.date.DateUtil;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.MailUtil;
import com.example.lotterysystem.common.utils.SMSUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.WinningRecordDO;
import com.example.lotterysystem.dao.mapper.ActivityPrizeMapper;
import com.example.lotterysystem.dao.mapper.WinningRecordMapper;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.activitystatus.ActivityStatusManager;
import com.example.lotterysystem.service.dto.ConvertActivityStatusDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import com.example.lotterysystem.service.impl.DrawPrizeServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;

@Component
@RabbitListener(queues = QUEUE_NAME)
public class MqReceier {

    private final static Logger logger= LoggerFactory.getLogger(MqReceier.class);

    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private DrawPrizeService drawPrizeService;

    @Autowired
    private ActivityStatusManager activityStatusManager;

    @Autowired
private MailUtil mailUtil;
    private SMSUtil smsUtil;

    @RabbitHandler
    public void receiveMessage(Map<String,String> message){
        //成功接收到队列中的消息
        logger.info("成功接收到队列中的消息:{}", JacksonUtil.writeValueAsString(message));
        String paramString= message.get("messageData");
        DrawPrizeParam param=JacksonUtil.readValue(paramString,DrawPrizeParam.class);
        //处理抽奖流程

        try{
            //效验抽奖请求是否有效
            //1.前端发起两个一样的抽奖请求，对于param来说也是一样的两个请求
            //2.param：param是最后一个奖项
            //        处理pamar将之前的修改回滚掉，等于没有修改
            if(!drawPrizeService.checkDrawPrizeParam(param)){
                return;
            }
                        ;
            //状态扭转处理
            statusConvert(param);
            //保存中奖名单
            List<WinningRecordDO> winningRecordDOList =
                    drawPrizeService.saveWinnerList(param);


            //通知中奖者（短信,邮箱）
            //抽奖之后的后续流程，异步（并发）处理
            syncExecute(winningRecordDOList);

        }catch (ServiceException e){
            logger.error("出来MQ消息处理异常！:{}：{}",e.getCode(),e.getMessage(),e);
            //如果出现异常需要保持事务一致性（事务回滚），
            // 抛出异常:消息重试（解决）
            rollback(param);
            throw e;
        } catch (Exception e) {
            logger.error("处理 MQ 消息异常！", e);
            // 需要保证事务一致性（回滚）
            rollback(param);
            // 抛出异常
            throw e;
        }

    }

    /**
     * 处理抽奖异常的回滚行为：恢复处理请求之前的库表数据
     * @param param
     */
    private void rollback(DrawPrizeParam param) {
       // 1.回滚状态：活动，奖品，人员
        //状态是否需要回滚
        if(!statusNeedRollback(param)){
            //不需要：return
            return;
        }
            //需要：状态回滚
        rollbackStatus(param);


        if(!winnerNeedRollback(param)){
            //不需要：return
            return;
        }
        //需要：状态回滚
rollbackWinner(param);



        //2.回滚中奖者名单
        //状态是否需要回滚
        //不需要：return

        //需要：状态回滚

    }


    /**
     * 回滚中奖者名单
     * @param param
     */
    private void rollbackWinner(DrawPrizeParam param) {
drawPrizeService.deleteList(param.getActivityId(),param.getPrizeId());
    }

    private boolean winnerNeedRollback(DrawPrizeParam param) {
        //判断活动中的奖品是否存在中奖者
int count=winningRecordMapper.countByAPId(param.getActivityId(),param.getPrizeId());
        return count>0;
    }

    /**
     * 恢复相关状态
     * @param param
     */

    private void rollbackStatus(DrawPrizeParam param) {
        // 涉及状态的恢复，使用 ActivityStatusManager
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.INIT);
        convertActivityStatusDTO.setUserIdList(
                param.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.INIT);
        activityStatusManager.rollbackHandlerEvent(convertActivityStatusDTO);

    }

    private boolean statusNeedRollback(DrawPrizeParam param) {
        //判断活动+奖品+人员表相关状态是否已经扭转
        // 扭转状态时，保证了事务一致性，要么都扭转了，要么都没扭转（不包含活动）：
        // 因此，只用判断人员/奖品是否扭转过，就能判断出状态是否全部扭转
        // 不能判断活动是否已经扭转
        // 结论：判断奖品状态是否扭转，就能判断出全部状态是否扭转
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        return activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.FINISHED.name());

    }

    private void syncExecute(List<WinningRecordDO> winningRecordDOList) {
//通过线程池 threadPoolTaskExecutor 执行

        //TODO 短信通知
        threadPoolTaskExecutor.execute(()->sendMessage(winningRecordDOList));

        //邮件通知
        threadPoolTaskExecutor.execute(()->sendMail(winningRecordDOList));



    }

    private void sendMessage(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)){
            logger.info("中奖名单为空，不发送短信通知");
            return;
        }
        for (WinningRecordDO winningRecordDO:winningRecordDOList) {
            smsUtil.sendSMS(winningRecordDO.getWinnerPhone().getValue(),"0000","1");
        }

    }

    private void sendMail(List<WinningRecordDO> winningRecordDOList) {
if(CollectionUtils.isEmpty(winningRecordDOList)){
    logger.info("中奖名单为空，不发送邮件通知");
    return;
}
        for (WinningRecordDO winningRecordDO:winningRecordDOList) {
            String context = "Hi，" + winningRecordDO.getWinnerName() + "。恭喜你在"
                    + winningRecordDO.getActivityName() + "活动中获得"
                    + ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTiers()).getMessage()
                    + "：" + winningRecordDO.getPrizeName() + "。获奖时间为"
                    + DateUtil.formatTime(winningRecordDO.getWinningTime()) + "，请尽快领 取您的奖励！";

mailUtil.sendSampleMail(winningRecordDO.getWinnerEmail(),
        "中奖通知", context);
        }

    }


    /**
     * 状态扭转
     * @param param
     */
    private void statusConvert(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO=new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.FINISHED);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.FINISHED);
        convertActivityStatusDTO.setUserIdList(
                param.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.FINISHED);
        activityStatusManager.handleEvent(convertActivityStatusDTO);
    }



//    private void statusConvert(DrawPrizeParam param) {


        //活动: RUNNING-->FINISH??全部奖品抽完之后才改变状态
        //奖品：INIT-->FINISH
        //人员列表：INIT-->FINISH

        //1、扭转奖品状态
        //查看活动关联的奖品信息
        //判断当前状态是否是COMPLETED，如果不是，要扭转

        //2、扭转人员状态
        //查看活动关联的人员信息
        //判断当前状态是否是COMPLETED，如果不是，要扭转



        //3、扭转活动状态
        //查看活动关联的奖品信息
        //判断是否符合活动扭转活动状态，判断当前状态是否不是compleTed，如果不是，且全部奖品1抽完之后才改变状态

        //更新活动完整信息缓存

//    }

}
